package server

import (
	"context"
	"crypto/tls"
	"github.com/elazarl/go-bindata-assetfs"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/reflection"
	"grpc-gateway-demo/pkg/ui/data/swagger"
	"grpc-gateway-demo/pkg/util"
	"grpc-gateway-demo/proto/pb"
	"log"
	"net"
	"net/http"
	"path"
	"strings"
)

var (
	Port        string
	CertName    string
	CertPemPath string
	CertKeyPath string
	SwaggerDir  string
)

func Server() (err error) {
	log.Println(Port, CertName, CertPemPath, CertKeyPath)

	// 监听本地的网络地址
	address := ":" + Port
	conn, err := net.Listen("tcp", address)
	if err != nil {
		log.Printf("TCP Listen err: %v\n", err)
		return err
	}

	tlsConfig := util.GetTLSConfig(CertPemPath, CertKeyPath)

	server := createInternalServer(address, tlsConfig)
	log.Printf("gRPC and https listen on:%s\n", Port)
	newListener := tls.NewListener(conn, tlsConfig)
	if err = server.Serve(newListener); err != nil {
		log.Printf("Listen and Server err: %v\n", err)
		return err
	}
	return nil
}

/**
createInternalServer: 创建grpc服务
*/
func createInternalServer(address string, tlsConfig *tls.Config) *http.Server {
	var opts []grpc.ServerOption

	// grpc server
	cred, err := credentials.NewServerTLSFromFile(CertPemPath, CertKeyPath)
	if err != nil {
		log.Printf("Failed to create server TLS credentials: %v\n", err)
	}

	opts = append(opts, grpc.Creds(cred))
	server := grpc.NewServer(opts...)

	// reflection register
	reflection.Register(server)

	// register grpc pb
	pb.RegisterHelloWorldServer(server, NewHelloWorldService())

	// gateway server
	ctx := context.Background()
	newCred, err := credentials.NewClientTLSFromFile(CertPemPath, CertName)
	if err != nil {
		log.Printf("Failed to create client TLS credentials: %v\n", err)
	}
	dialOpt := []grpc.DialOption{grpc.WithTransportCredentials(newCred)}
	gateWayMux := runtime.NewServeMux()

	// register grpc-gateway pb
	if err := pb.RegisterHelloWorldHandlerFromEndpoint(ctx, gateWayMux, address, dialOpt); err != nil {
		log.Printf("Failed to register gateway server: %v\n", err)
	}

	mux := http.NewServeMux()
	mux.Handle("/", gateWayMux)

	// register swagger
	mux.HandleFunc("/swagger/", swaggerFile)
	swaggerUI(mux)

	return &http.Server{
		Addr:      address,
		Handler:   util.GrpcHandlerFunc(server, mux),
		TLSConfig: tlsConfig,
	}
}

/**
swaggerFile: 提供对swagger.json文件的访问支持
*/
func swaggerFile(w http.ResponseWriter, r *http.Request) {
	if !strings.HasSuffix(r.URL.Path, "swagger.json") {
		log.Printf("Not Found: %s", r.URL.Path)
		http.NotFound(w, r)
		return
	}

	p := strings.TrimPrefix(r.URL.Path, "/swagger/")
	name := path.Join(SwaggerDir, p)
	log.Printf("Serving swagger-file: %s", name)
	http.ServeFile(w, r, name)
}

/**
swaggerUI: 提供UI支持
*/
func swaggerUI(mux *http.ServeMux) {
	fileServer := http.FileServer(&assetfs.AssetFS{
		Asset:    swagger.Asset,
		AssetDir: swagger.AssetDir,
	})
	prefix := "/swagger-ui/"
	mux.Handle(prefix, http.StripPrefix(prefix, fileServer))
}
